package com.ctrip.corp.obt.shard.utils;

import static com.ctrip.corp.obt.shard.sql.parser.fuzzy.SecurityFuzzyQueryHandler.COLUMN_JOIN;
import static com.ctrip.corp.obt.shard.sql.parser.fuzzy.SecurityFuzzyQueryHandler.SECRET_DIVIDE_DEFAULT_COLUMN_PREFIX;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

import javax.annotation.Nullable;

import com.ctrip.corp.obt.generic.utils.StringUtils;
import com.ctrip.corp.obt.shard.annotation.SecurityFuzzyQueryField;
import com.ctrip.corp.obt.shard.sql.parser.fuzzy.SecurityFuzzyQueryHandler;
import com.google.common.collect.Lists;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**
 * 模糊查询 历史数据同步 工具类
 * 
 * @author xukang zhang
 * @date 2024/1/4 13:10
 */
@Slf4j
public class SecurityFuzzyQueryUtils {

    /**
     * 依据SecurityFuzzyQueryField注解的配置，对content进行分词加密
     * 
     * @param securityFuzzyQueryField
     * @param content
     * @return
     */
    public static String encryptWithDividing(SecurityFuzzyQueryField securityFuzzyQueryField, String content) {
        if (securityFuzzyQueryField == null) {
            return null;
        }
        if (StringUtils.isBlank(content)) {
            return StringUtils.EMPTY;
        }

        // 分词加密
        String encryptAlgorithm = securityFuzzyQueryField.encryptAlgorithm();
        int divideLength = securityFuzzyQueryField.divideLength();
        String encryptedContent = null;
        try {
            encryptedContent = SecurityFuzzyQueryHandler.encryptWithDividing(content, encryptAlgorithm, divideLength);
        } catch (Exception e) {
            return null;
        }
        return encryptedContent;
    }

    /**
     *
     * @param entity 对应数据库表的实体类
     * @param originTableName entity的原始表名
     * @param originDecryptFunction 原始列内容解密函数
     * @return
     * @param <T>
     */
    public static <T> List<DividingColumnInfo> encryptWithDividingByEntity(T entity, String originTableName, Function<String, String> originDecryptFunction) {
        return encryptWithDividingByEntity(entity, null, originTableName, originDecryptFunction);
    }

    /**
     *
     * @param entity 对应数据库表的实体类
     * @param originColumnNames entity中属性对应的原始列名 (仅在不设置自定义分词列名 并且 原始列名与属性名不一致时使用)
     * @param originTableName entity的原始表名
     * @param originDecryptFunction 原始列内容解密函数
     * @return
     * @param <T>
     */
    public static <T> List<DividingColumnInfo> encryptWithDividingByEntity(T entity,
        @Nullable Map<String, String> originColumnNames, String originTableName, Function<String, String> originDecryptFunction) {
        if (entity == null) {
            return null;
        }
        List<DividingColumnInfo> dividingColumnInfos = Lists.newArrayList();
        Field[] fields = entity.getClass().getDeclaredFields();

        for (Field field : fields) {
            SecurityFuzzyQueryField securityFuzzyQueryField = field.getAnnotation(SecurityFuzzyQueryField.class);
            if (securityFuzzyQueryField == null) {
                continue;
            }
            String fieldName = field.getName();
            String divideTable = securityFuzzyQueryField.divideTable();
            String divideColumn = securityFuzzyQueryField.divideColumn();
            String content = null;
            try {
                field.setAccessible(true);
                content = (String)field.get(entity);
            } catch (IllegalAccessException e) {
                continue;
            }
            if (StringUtils.isBlank(content)) {
                continue;
            }

            // 原字段进行解密
            if (originDecryptFunction != null) {
                try {
                    content = originDecryptFunction.apply(content);
                } catch (Exception e) {
                    log.error("Original column content decrypt failed, field: {}, content: {}", field, content);
                    continue;
                }
            }

            // 加密
            String encryptedContent = encryptWithDividing(securityFuzzyQueryField, content);
            if (StringUtils.isBlank(encryptedContent)) {
                log.error("Dividing column content encrypt failed, field: {}, content: {}", field, content);
                continue;
            }

            // 表名
            if (StringUtils.isBlank(divideTable)) {
                divideTable = originTableName;
            }

            // 列名
            if (StringUtils.isBlank(divideColumn)) {
                // 使用默认列名 ${columnName}_secret_divide_${divideNumber}
                String originalColumnName =
                    Optional.ofNullable(originColumnNames).map(o -> o.get(fieldName)).orElse(null);
                if (StringUtils.isBlank(originalColumnName)) {
                    // 未提供原始列名，则默认采用属性名 并转换为下划线分隔命名法
                    originalColumnName = StringUtils.camelToUnderline(fieldName);
                }
                divideColumn =
                    originalColumnName + COLUMN_JOIN + SECRET_DIVIDE_DEFAULT_COLUMN_PREFIX + COLUMN_JOIN
                        + securityFuzzyQueryField.divideLength();
            }

            DividingColumnInfo dividingColumnInfo = new DividingColumnInfo();
            dividingColumnInfo.setColumnName(divideColumn);
            dividingColumnInfo.setDivideTable(divideTable);
            dividingColumnInfo.setContent(encryptedContent);
            dividingColumnInfos.add(dividingColumnInfo);
        }
        return dividingColumnInfos;
    }


    /**
     * 分词加密列信息
     */
    @Data
    public static class DividingColumnInfo {
        private String columnName;

        private String divideTable;

        private String content;

    }
}
